home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / www / src / midaswww-1.0 / Tree.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-16  |  29.2 KB  |  1,046 lines

  1. /*
  2.  * $XConsortium: Tree.c,v 1.42 91/02/20 20:06:07 converse Exp $
  3.  *
  4.  * Copyright 1990 Massachusetts Institute of Technology
  5.  * Copyright 1989 Prentice Hall
  6.  *
  7.  * Permission to use, copy, modify, and distribute this software for any
  8.  * purpose and without fee is hereby granted, provided that the above
  9.  * copyright notice appear in all copies and that both the copyright notice
  10.  * and this permission notice appear in supporting documentation.
  11.  * 
  12.  * M.I.T., Prentice Hall and the authors disclaim all warranties with regard
  13.  * to this software, including all implied warranties of merchantability and
  14.  * fitness.  In no event shall M.I.T., Prentice Hall or the authors be liable
  15.  * for any special, indirect or cosequential damages or any damages whatsoever
  16.  * resulting from loss of use, data or profits, whether in an action of
  17.  * contract, negligence or other tortious action, arising out of or in
  18.  * connection with the use or performance of this software.
  19.  * 
  20.  * Authors:  Jim Fulton, MIT X Consortium,
  21.  *           based on a version by Douglas Young, Prentice Hall
  22.  * 
  23.  * This widget is based on the Tree widget described on pages 397-419 of
  24.  * Douglas Young's book "The X Window System, Programming and Applications 
  25.  * with Xt OSF/Motif Edition."  The layout code has been rewritten to use
  26.  * additional blank space to make the structure of the graph easier to see
  27.  * as well as to support vertical trees.
  28.  */
  29.  
  30. #include <X11/Intrinsic.h>
  31. #include <X11/IntrinsicP.h>
  32. #include <X11/StringDefs.h>
  33. #include <X11/CoreP.h>
  34. #include <X11/CompositeP.h>
  35. #include <X11/ConstrainP.h>
  36. #include "TreeP.h"
  37.  
  38. #define IsHorizontal(tw) ((tw)->tree.gravity == WestGravity || \
  39.               (tw)->tree.gravity == EastGravity)
  40.  
  41.  
  42.                     /* widget class method */
  43. static void             ClassInitialize();
  44. static void             Initialize();
  45. static void             ConstraintInitialize();
  46. static void             ConstraintDestroy();
  47. static Boolean          ConstraintSetValues();
  48. static void             Destroy();
  49. static Boolean          SetValues();
  50. static XtGeometryResult GeometryManager();
  51. static void             ChangeManaged();
  52. static void             Redisplay();
  53. static XtGeometryResult    QueryGeometry();
  54.  
  55.                     /* utility routines */
  56. static void             insert_node();
  57. static void             delete_node();
  58. static void             layout_tree();
  59.  
  60.  
  61. /*
  62.  * resources of the tree itself
  63.  */
  64. static XtResource resources[] = {
  65.     { XtNautoReconfigure, XtCAutoReconfigure, XtRBoolean, sizeof (Boolean),
  66.     XtOffsetOf(TreeRec, tree.auto_reconfigure), XtRImmediate,
  67.     (XtPointer) FALSE },
  68.     { XtNhSpace, XtCHSpace, XtRDimension, sizeof (Dimension),
  69.     XtOffsetOf(TreeRec, tree.hpad), XtRImmediate, (XtPointer) 0 },
  70.     { XtNvSpace, XtCVSpace, XtRDimension, sizeof (Dimension),
  71.     XtOffsetOf(TreeRec, tree.vpad), XtRImmediate, (XtPointer) 0 },
  72.     { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
  73.     XtOffsetOf(TreeRec, tree.foreground), XtRString,
  74.     XtDefaultForeground},
  75.     { XtNlineWidth, XtCLineWidth, XtRDimension, sizeof (Dimension),
  76.     XtOffsetOf(TreeRec, tree.line_width), XtRImmediate, (XtPointer) 0 },
  77.     { XtNgravity, XtCGravity, XtRGravity, sizeof (XtGravity),
  78.     XtOffsetOf(TreeRec, tree.gravity), XtRImmediate,
  79.     (XtPointer) WestGravity },
  80. };
  81.  
  82.  
  83. /*
  84.  * resources that are attached to all children of the tree
  85.  */
  86. static XtResource treeConstraintResources[] = {
  87.     { XtNtreeParent, XtCTreeParent, XtRWidget, sizeof (Widget),
  88.     XtOffsetOf(TreeConstraintsRec, tree.parent), XtRImmediate, NULL },
  89.     { XtNtreeGC, XtCTreeGC, XtRGC, sizeof(GC),
  90.     XtOffsetOf(TreeConstraintsRec, tree.gc), XtRImmediate, NULL },
  91. };
  92.  
  93.  
  94. TreeClassRec treeClassRec = {
  95.   {
  96.                     /* core_class fields  */
  97.     (WidgetClass) &constraintClassRec,  /* superclass         */
  98.     "Tree",                /* class_name         */
  99.     sizeof(TreeRec),            /* widget_size        */
  100.     ClassInitialize,            /* class_init         */
  101.     NULL,                /* class_part_init    */
  102.     FALSE,                /* class_inited       */    
  103.     Initialize,                /* initialize         */
  104.     NULL,                /* initialize_hook    */    
  105.     XtInheritRealize,            /* realize            */
  106.     NULL,                /* actions            */
  107.     0,                    /* num_actions        */    
  108.     resources,                /* resources          */
  109.     XtNumber(resources),        /* num_resources      */
  110.     NULLQUARK,                /* xrm_class          */
  111.     TRUE,                /* compress_motion    */    
  112.     TRUE,                /* compress_exposure  */    
  113.     TRUE,                /* compress_enterleave*/    
  114.     TRUE,                /* visible_interest   */
  115.     Destroy,                /* destroy            */
  116.     NULL,                /* resize             */
  117.     Redisplay,                /* expose             */
  118.     SetValues,                /* set_values         */
  119.     NULL,                /* set_values_hook    */    
  120.     XtInheritSetValuesAlmost,        /* set_values_almost  */
  121.     NULL,                /* get_values_hook    */    
  122.     NULL,                /* accept_focus       */
  123.     XtVersion,                /* version            */    
  124.     NULL,                /* callback_private   */
  125.     NULL,                /* tm_table           */
  126.     QueryGeometry,            /* query_geometry     */    
  127.     NULL,                /* display_accelerator*/
  128.     NULL,                /* extension          */
  129.   },
  130.   {
  131.                     /* composite_class fields */
  132.     GeometryManager,            /* geometry_manager    */
  133.     ChangeManaged,            /* change_managed      */
  134.     XtInheritInsertChild,        /* insert_child        */    
  135.     XtInheritDeleteChild,        /* delete_child        */    
  136.     NULL,                /* extension           */
  137.   },
  138.   { 
  139.                     /* constraint_class fields */
  140.    treeConstraintResources,        /* subresources        */
  141.    XtNumber(treeConstraintResources),    /* subresource_count   */
  142.    sizeof(TreeConstraintsRec),        /* constraint_size     */
  143.    ConstraintInitialize,        /* initialize          */
  144.    ConstraintDestroy,            /* destroy             */
  145.    ConstraintSetValues,            /* set_values          */
  146.    NULL,                /* extension           */
  147.    },
  148.   {
  149.                     /* Tree class fields */
  150.     0,                    /* ignore              */    
  151.   }
  152. };
  153.  
  154. WidgetClass treeWidgetClass = (WidgetClass) &treeClassRec;
  155.  
  156.  
  157. /*****************************************************************************
  158.  *                                                                           *
  159.  *                 tree utility routines                           *
  160.  *                                                                           *
  161.  *****************************************************************************/
  162.  
  163. static void initialize_dimensions (listp, sizep, n)
  164.     Dimension **listp;
  165.     int *sizep;
  166.     int n;
  167. {
  168.     register int i;
  169.     register Dimension *l;
  170.  
  171.     if (!*listp) {
  172.     *listp = (Dimension *) XtCalloc ((unsigned int) n,
  173.                      (unsigned int) sizeof(Dimension));
  174.     *sizep = ((*listp) ? n : 0);
  175.     return;
  176.     }
  177.     if (n > *sizep) {
  178.     *listp = (Dimension *) XtRealloc((char *) *listp,
  179.                      (unsigned int) (n*sizeof(Dimension)));
  180.     if (!*listp) {
  181.         *sizep = 0;
  182.         return;
  183.     }
  184.     for (i = *sizep, l = (*listp) + i; i < n; i++, l++) *l = 0;
  185.     *sizep = n;
  186.     }
  187.     return;
  188. }
  189.  
  190. static GC get_tree_gc (w)
  191.     TreeWidget w;
  192. {
  193.     XtGCMask valuemask = GCBackground | GCForeground;
  194.     XGCValues values;
  195.  
  196.     values.background = w->core.background_pixel;
  197.     values.foreground = w->tree.foreground;
  198.     if (w->tree.line_width != 0) {
  199.     valuemask |= GCLineWidth;
  200.     values.line_width = w->tree.line_width;
  201.     }
  202.  
  203.     return XtGetGC ((Widget) w, valuemask, &values);
  204. }
  205.  
  206. static void insert_node (parent, node)
  207.      Widget parent, node;
  208. {
  209.     TreeConstraints pc;
  210.     TreeConstraints nc = TREE_CONSTRAINT(node);
  211.     int nindex;
  212.   
  213.     nc->tree.parent = parent;
  214.  
  215.     if (parent == NULL) return;
  216.  
  217.     /*
  218.      * If there isn't more room in the children array, 
  219.      * allocate additional space.
  220.      */  
  221.     pc = TREE_CONSTRAINT(parent);
  222.     nindex = pc->tree.n_children;
  223.   
  224.     if (pc->tree.n_children == pc->tree.max_children) {
  225.     pc->tree.max_children += (pc->tree.max_children / 2) + 2;
  226.     pc->tree.children = (WidgetList) XtRealloc ((char *)pc->tree.children, 
  227.                             (unsigned int)
  228.                             ((pc->tree.max_children) *
  229.                             sizeof(Widget)));
  230.     } 
  231.  
  232.     /*
  233.      * Add the sub_node in the next available slot and 
  234.      * increment the counter.
  235.      */
  236.     pc->tree.children[nindex] = node;
  237.     pc->tree.n_children++;
  238. }
  239.  
  240. static void delete_node (parent, node)
  241.     Widget parent, node;
  242. {
  243.     TreeConstraints pc;
  244.     int pos, i;
  245.  
  246.     /*
  247.      * Make sure the parent exists.
  248.      */
  249.     if (!parent) return;  
  250.   
  251.     pc = TREE_CONSTRAINT(parent);
  252.  
  253.     /*
  254.      * Find the sub_node on its parent's list.
  255.      */
  256.     for (pos = 0; pos < pc->tree.n_children; pos++)
  257.       if (pc->tree.children[pos] == node) break;
  258.  
  259.     if (pos == pc->tree.n_children) return;
  260.  
  261.     /*
  262.      * Decrement the number of children
  263.      */  
  264.     pc->tree.n_children--;
  265.  
  266.     /*
  267.      * Fill in the gap left by the sub_node.
  268.      * Zero the last slot for good luck.
  269.      */
  270.     for (i = pos; i < pc->tree.n_children; i++) 
  271.       pc->tree.children[i] = pc->tree.children[i+1];
  272.  
  273.     pc->tree.children[pc->tree.n_children]=0;
  274. }
  275.  
  276. static void check_gravity (tw, grav)
  277.     TreeWidget tw;
  278.     XtGravity grav;
  279. {
  280.     switch (tw->tree.gravity) {
  281.       case WestGravity: case NorthGravity: case EastGravity: case SouthGravity:
  282.     break;
  283.       default:
  284.     tw->tree.gravity = grav;
  285.     break;
  286.     }
  287. }
  288. #define done(address, type) \
  289.         { (*toVal).size = sizeof(type); (*toVal).addr = (caddr_t) address; }
  290.  
  291. static struct _namepair {
  292.     XrmQuark quark;
  293.     char *name;
  294.     XtGravity gravity;
  295. } names[] = {
  296.     { NULLQUARK, XtEForget, ForgetGravity },
  297.     { NULLQUARK, XtENorthWest, NorthWestGravity },
  298.     { NULLQUARK, XtENorth, NorthGravity },
  299.     { NULLQUARK, XtENorthEast, NorthEastGravity },
  300.     { NULLQUARK, XtEWest, WestGravity },
  301.     { NULLQUARK, XtECenter, CenterGravity },
  302.     { NULLQUARK, XtEEast, EastGravity },
  303.     { NULLQUARK, XtESouthWest, SouthWestGravity },
  304.     { NULLQUARK, XtESouth, SouthGravity },
  305.     { NULLQUARK, XtESouthEast, SouthEastGravity },
  306.     { NULLQUARK, XtEStatic, StaticGravity },
  307.     { NULLQUARK, XtEUnmap, UnmapGravity },
  308.     { NULLQUARK, XtEleft, WestGravity },
  309.     { NULLQUARK, XtEtop, NorthGravity },
  310.     { NULLQUARK, XtEright, EastGravity },
  311.     { NULLQUARK, XtEbottom, SouthGravity },
  312.     { NULLQUARK, NULL, ForgetGravity }
  313. };
  314.  
  315. void CvtStringToGravity (args, num_args, fromVal, toVal)
  316.     XrmValuePtr args;
  317.     Cardinal    *num_args;
  318.     XrmValuePtr fromVal;
  319.     XrmValuePtr toVal;
  320. {
  321.     static Boolean haveQuarks = FALSE;
  322.     char lowerName[40];
  323.     XrmQuark q;
  324.     char *s, *t;
  325.     struct _namepair *np;
  326.  
  327.     if (*num_args != 0)
  328.         XtWarningMsg("wrongParameters","cvtStringToGravity","XtToolkitError",
  329.                   "String to Gravity conversion needs no extra arguments",
  330.                   (String *) NULL, (Cardinal *)NULL);
  331.  
  332.     if (!haveQuarks) {
  333.     for (np = names; np->name; np++) {
  334.         np->quark = XrmStringToQuark (np->name);
  335.     }
  336.     haveQuarks = TRUE;
  337.     }
  338.  
  339.     s = (char *) fromVal->addr;
  340.     if (strlen(s) < sizeof lowerName) {
  341.     for (t=lowerName;  (*t++ = tolower(*s++)) != '\0'; );
  342.     q = XrmStringToQuark (lowerName);
  343.  
  344.     for (np = names; np->name; np++) {
  345.         if (np->quark == q) {
  346.         done (&np->gravity, XtGravity);
  347.         return;
  348.         }
  349.     }
  350.     }
  351.     XtStringConversionWarning((char *) fromVal->addr, XtRGravity);
  352. }
  353.  
  354.  
  355.  
  356.  
  357.  
  358. /*****************************************************************************
  359.  *                                                                           *
  360.  *                   tree class methods                             *
  361.  *                                                                           *
  362.  *****************************************************************************/
  363.  
  364. static void ClassInitialize ()
  365. {
  366.     XtAddConverter (XtRString, XtRGravity, CvtStringToGravity,
  367.                 (XtConvertArgList) NULL, (Cardinal) 0);
  368. }
  369.  
  370.  
  371. static void Initialize (grequest, gnew)
  372.     Widget grequest, gnew;
  373. {
  374.     TreeWidget request = (TreeWidget) grequest, new = (TreeWidget) gnew;
  375.     Arg args[2];
  376.  
  377.     /*
  378.      * Make sure the widget's width and height are 
  379.      * greater than zero.
  380.      */
  381.     if (request->core.width <= 0) new->core.width = 5;
  382.     if (request->core.height <= 0) new->core.height = 5;
  383.  
  384.     /*
  385.      * Set the padding according to the orientation
  386.      */
  387.     if (request->tree.hpad == 0 && request->tree.vpad == 0) {
  388.     if (IsHorizontal (request)) {
  389.         new->tree.hpad = TREE_HORIZONTAL_DEFAULT_SPACING;
  390.         new->tree.vpad = TREE_VERTICAL_DEFAULT_SPACING;
  391.     } else {
  392.         new->tree.hpad = TREE_VERTICAL_DEFAULT_SPACING;
  393.         new->tree.vpad = TREE_HORIZONTAL_DEFAULT_SPACING;
  394.     }
  395.     }
  396.  
  397.     /*
  398.      * Create a graphics context for the connecting lines.
  399.      */
  400.     new->tree.gc = get_tree_gc (new);
  401.  
  402.     /*
  403.      * Create the hidden root widget.
  404.      */
  405.     new->tree.tree_root = (Widget) NULL;
  406.     XtSetArg(args[0], XtNwidth, 1);
  407.     XtSetArg(args[1], XtNheight, 1);
  408.     new->tree.tree_root = XtCreateWidget ("root", widgetClass, gnew, args,((Cardinal) 2));
  409.     
  410.     /*
  411.      * Allocate the array used to hold the widest values per depth
  412.      */
  413.     new->tree.largest = NULL;
  414.     new->tree.n_largest = 0;
  415.     initialize_dimensions (&new->tree.largest, &new->tree.n_largest, 
  416.                TREE_INITIAL_DEPTH);
  417.  
  418.     /*
  419.      * make sure that our gravity is one of the acceptable values
  420.      */
  421.     check_gravity (new, WestGravity);
  422.  
  423.  
  424. /* ARGSUSED */
  425. static void ConstraintInitialize (request, new)
  426.      Widget request, new;
  427. {
  428.     TreeConstraints tc = TREE_CONSTRAINT(new);
  429.     TreeWidget tw = (TreeWidget) new->core.parent;
  430.  
  431.     /*
  432.      * Initialize the widget to have no sub-nodes.
  433.      */
  434.     tc->tree.n_children = 0;
  435.     tc->tree.max_children = 0;
  436.     tc->tree.children = (Widget *) NULL;
  437.     tc->tree.x = tc->tree.y = 0; 
  438.     tc->tree.bbsubwidth = 0;
  439.     tc->tree.bbsubheight = 0;
  440.  
  441.  
  442.     /*
  443.      * If this widget has a super-node, add it to that 
  444.      * widget' sub-nodes list. Otherwise make it a sub-node of 
  445.      * the tree_root widget.
  446.      */
  447.     if (tc->tree.parent)
  448.       insert_node (tc->tree.parent, new);
  449.     else if (tw->tree.tree_root)
  450.       insert_node (tw->tree.tree_root, new);
  451.  
  452.  
  453. /* ARGSUSED */
  454. static Boolean SetValues (gcurrent, grequest, gnew)
  455.     Widget gcurrent, grequest, gnew;
  456. {
  457.     TreeWidget current = (TreeWidget) gcurrent, new = (TreeWidget) gnew;
  458.     Boolean redraw = FALSE;
  459.  
  460.     /*
  461.      * If the foreground color has changed, redo the GC's
  462.      * and indicate a redraw.
  463.      */
  464.     if (new->tree.foreground != current->tree.foreground ||
  465.     new->core.background_pixel != current->core.background_pixel ||
  466.     new->tree.line_width != current->tree.line_width) {
  467.     XtReleaseGC (gnew, new->tree.gc);
  468.     new->tree.gc = get_tree_gc (new);
  469.     redraw = TRUE;     
  470.     }
  471.  
  472.     /*
  473.      * If the minimum spacing has changed, recalculate the
  474.      * tree layout. layout_tree() does a redraw, so we don't
  475.      * need SetValues to do another one.
  476.      */
  477.     if (new->tree.gravity != current->tree.gravity) {
  478.     check_gravity (new, current->tree.gravity);
  479.     }
  480.  
  481.     if (IsHorizontal(new) != IsHorizontal(current)) {
  482.     if (new->tree.vpad == current->tree.vpad &&
  483.         new->tree.hpad == current->tree.hpad) {
  484.         new->tree.vpad = current->tree.hpad;
  485.         new->tree.hpad = current->tree.vpad;
  486.     }
  487.     }
  488.  
  489.     if (new->tree.vpad != current->tree.vpad ||
  490.     new->tree.hpad != current->tree.hpad ||
  491.     new->tree.gravity != current->tree.gravity) {
  492.     layout_tree (new, TRUE);
  493.     redraw = FALSE;
  494.     }
  495.     return redraw;
  496. }
  497.  
  498.  
  499. /* ARGSUSED */
  500. static Boolean ConstraintSetValues (current, request, new, args, num_args)
  501.     Widget current, request, new;
  502.     ArgList args;
  503.     Cardinal *num_args;
  504. {
  505.     TreeConstraints newc = TREE_CONSTRAINT(new);
  506.     TreeConstraints curc = TREE_CONSTRAINT(current);
  507.     TreeWidget tw = (TreeWidget) new->core.parent;
  508.  
  509.     /*
  510.      * If the parent field has changed, remove the widget
  511.      * from the old widget's children list and add it to the
  512.      * new one.
  513.      */
  514.     if (curc->tree.parent != newc->tree.parent){
  515.     if (curc->tree.parent)
  516.       delete_node (curc->tree.parent, new);
  517.     if (newc->tree.parent)
  518.       insert_node(newc->tree.parent, new);
  519.  
  520.     /*
  521.      * If the Tree widget has been realized, 
  522.      * compute new layout.
  523.      */
  524.     if (XtIsRealized((Widget)tw))
  525.       layout_tree (tw, FALSE);
  526.     }
  527.     return False;
  528. }
  529.  
  530.  
  531. static void ConstraintDestroy (w) 
  532.     Widget w;
  533.     TreeConstraints tc = TREE_CONSTRAINT(w);
  534.     TreeWidget tw = (TreeWidget) XtParent(w);
  535.     int i;
  536.  
  537.     /* 
  538.      * Remove the widget from its parent's sub-nodes list and
  539.      * make all this widget's sub-nodes sub-nodes of the parent.
  540.      */
  541.   
  542.     if (tw->tree.tree_root == w) {
  543.     if (tc->tree.n_children > 0)
  544.       tw->tree.tree_root = tc->tree.children[0];
  545.     else
  546.       tw->tree.tree_root = NULL;
  547.     }
  548.  
  549.     delete_node (tc->tree.parent, (Widget) w);
  550.     for (i = 0; i< tc->tree.n_children; i++)
  551.       insert_node (tc->tree.parent, tc->tree.children[i]);
  552.  
  553.     layout_tree ((TreeWidget) (w->core.parent), FALSE);
  554. }
  555.  
  556. /* ARGSUSED */
  557. static XtGeometryResult GeometryManager (w, request, reply)
  558.     Widget w;
  559.     XtWidgetGeometry *request;
  560.     XtWidgetGeometry *reply;
  561. {
  562.  
  563.     TreeWidget tw = (TreeWidget) w->core.parent;
  564.  
  565.     /*
  566.      * No position changes allowed!.
  567.      */
  568.     if ((request->request_mode & CWX && request->x!=w->core.x)
  569.     ||(request->request_mode & CWY && request->y!=w->core.y))
  570.       return (XtGeometryNo);
  571.  
  572.     /*
  573.      * Allow all resize requests.
  574.      */
  575.  
  576.     if (request->request_mode & CWWidth)
  577.       w->core.width = request->width;
  578.     if (request->request_mode & CWHeight)
  579.       w->core.height = request->height;
  580.     if (request->request_mode & CWBorderWidth)
  581.       w->core.border_width = request->border_width;
  582.  
  583.     if (tw->tree.auto_reconfigure) layout_tree (tw, FALSE);
  584.     return (XtGeometryYes);
  585. }
  586.  
  587. static void ChangeManaged (gw)
  588.     Widget gw;
  589. {
  590.     layout_tree ((TreeWidget) gw, FALSE);
  591. }
  592.  
  593.  
  594. static void Destroy (gw)
  595.     Widget gw;
  596. {
  597.     TreeWidget w = (TreeWidget) gw;
  598.  
  599.     XtReleaseGC (gw, w->tree.gc);
  600.     if (w->tree.largest) XtFree ((char *) w->tree.largest);
  601. }
  602.  
  603.  
  604. /* ARGSUSED */
  605. static void Redisplay (tw, event, region)
  606.      TreeWidget tw;
  607.      XEvent *event;
  608.      Region region;
  609. {
  610.     /*
  611.      * If the Tree widget is visible, visit each managed child.
  612.      */
  613.     if (tw->core.visible) {
  614.     int i, j;
  615.     Display *dpy = XtDisplay (tw);
  616.     Window w = XtWindow (tw);
  617.  
  618.     for (i = 0; i < tw->composite.num_children; i++) {
  619.         register Widget child = tw->composite.children[i];
  620.         TreeConstraints tc = TREE_CONSTRAINT(child);
  621.  
  622.         /*
  623.          * Don't draw lines from the fake tree_root.
  624.          */
  625.         if (child != tw->tree.tree_root && tc->tree.n_children) {
  626.         int srcx = child->core.x + child->core.border_width;
  627.         int srcy = child->core.y + child->core.border_width;
  628.  
  629.         switch (tw->tree.gravity) {
  630.           case WestGravity:
  631.             srcx += child->core.width + child->core.border_width;
  632.             /* fall through */
  633.           case EastGravity:
  634.             srcy += child->core.height / 2;
  635.             break;
  636.  
  637.           case NorthGravity:
  638.             srcy += child->core.height + child->core.border_width;
  639.             /* fall through */
  640.           case SouthGravity:
  641.             srcx += child->core.width / 2;
  642.             break;
  643.         }
  644.  
  645.         for (j = 0; j < tc->tree.n_children; j++) {
  646.             register Widget k = tc->tree.children[j];
  647.             GC gc = (tc->tree.gc ? tc->tree.gc : tw->tree.gc);
  648.  
  649.             switch (tw->tree.gravity) {
  650.               case WestGravity:
  651.             /*
  652.              * right center to left center
  653.              */
  654.             XDrawLine (dpy, w, gc, srcx, srcy,
  655.                    (int) k->core.x,
  656.                    (k->core.y + ((int) k->core.border_width) +
  657.                     ((int) k->core.height) / 2));
  658.             break;
  659.  
  660.               case NorthGravity:
  661.             /*
  662.              * bottom center to top center
  663.              */
  664.             XDrawLine (dpy, w, gc, srcx, srcy,
  665.                    (k->core.x + ((int) k->core.border_width) +
  666.                     ((int) k->core.width) / 2),
  667.                    (int) k->core.y);
  668.             break;
  669.  
  670.               case EastGravity:
  671.             /*
  672.              * left center to right center
  673.              */
  674.             XDrawLine (dpy, w, gc, srcx, srcy,
  675.                    (k->core.x +
  676.                     (((int) k->core.border_width) << 1) +
  677.                     (int) k->core.width),
  678.                    (k->core.y + ((int) k->core.border_width) +
  679.                     ((int) k->core.height) / 2));
  680.             break;
  681.  
  682.               case SouthGravity:
  683.             /*
  684.              * top center to bottom center
  685.              */
  686.             XDrawLine (dpy, w, gc, srcx, srcy,
  687.                    (k->core.x + ((int) k->core.border_width) +
  688.                     ((int) k->core.width) / 2),
  689.                    (k->core.y +
  690.                     (((int) k->core.border_width) << 1) +
  691.                     (int) k->core.height));
  692.             break;
  693.             }
  694.         }
  695.         }
  696.     }
  697.     }
  698. }
  699.  
  700. static XtGeometryResult QueryGeometry (w, intended, preferred)
  701.     Widget w;
  702.     XtWidgetGeometry *intended, *preferred;
  703. {
  704.     register TreeWidget tw = (TreeWidget) w;
  705.  
  706.     preferred->request_mode = (CWWidth | CWHeight);
  707.     preferred->width = tw->tree.maxwidth;
  708.     preferred->height = tw->tree.maxheight;
  709.  
  710.     if (((intended->request_mode & (CWWidth | CWHeight)) ==
  711.      (CWWidth | CWHeight)) &&
  712.     intended->width == preferred->width &&
  713.     intended->height == preferred->height)
  714.       return XtGeometryYes;
  715.     else if (preferred->width == w->core.width &&
  716.              preferred->height == w->core.height)
  717.       return XtGeometryNo;
  718.     else
  719.       return XtGeometryAlmost;
  720. }
  721.  
  722.  
  723. /*****************************************************************************
  724.  *                                                                           *
  725.  *                 tree layout algorithm                           *
  726.  *                                                                           *
  727.  * Each node in the tree is "shrink-wrapped" with a minimal bounding         *
  728.  * rectangle, laid next to its siblings (with a small about of padding in    *
  729.  * between) and then wrapped with their parent.  Parents are centered about  *
  730.  * their children (or vice versa if the parent is larger than the children). *
  731.  *                                                                           *
  732.  *****************************************************************************/
  733.  
  734. static void compute_bounding_box_subtree (tree, w, depth)
  735.     TreeWidget tree;
  736.     Widget w;
  737.     int depth;
  738. {
  739.     TreeConstraints tc = TREE_CONSTRAINT(w);  /* info attached to all kids */
  740.     register int i;
  741.     Bool horiz = IsHorizontal (tree);
  742.     Dimension newwidth, newheight;
  743.     Dimension bw2 = w->core.border_width * 2;
  744.  
  745.     /*
  746.      * Set the max-size per level.
  747.      */
  748.     if (depth >= tree->tree.n_largest) {
  749.     initialize_dimensions (&tree->tree.largest,
  750.                    &tree->tree.n_largest, depth + 1);
  751.     }
  752.     newwidth = ((horiz ? w->core.width : w->core.height) + bw2);
  753.     if (tree->tree.largest[depth] < newwidth)
  754.       tree->tree.largest[depth] = newwidth;
  755.  
  756.  
  757.     /*
  758.      * initialize
  759.      */
  760.     tc->tree.bbwidth = w->core.width + bw2;
  761.     tc->tree.bbheight = w->core.height + bw2;
  762.  
  763.     if (tc->tree.n_children == 0) return;
  764.  
  765.     /*
  766.      * Figure the size of the opposite dimension (vertical if tree is 
  767.      * horizontal, else vice versa).  The other dimension will be set 
  768.      * in the second pass once we know the maximum dimensions.
  769.      */
  770.     newwidth = 0;
  771.     newheight = 0;
  772.     for (i = 0; i < tc->tree.n_children; i++) {
  773.     Widget child = tc->tree.children[i];
  774.     TreeConstraints cc = TREE_CONSTRAINT(child);
  775.         
  776.     compute_bounding_box_subtree (tree, child, depth + 1);
  777.  
  778.     if (horiz) {
  779.         if (newwidth < cc->tree.bbwidth) newwidth = cc->tree.bbwidth;
  780.         newheight += tree->tree.vpad + cc->tree.bbheight;
  781.     } else {
  782.         if (newheight < cc->tree.bbheight) newheight = cc->tree.bbheight;
  783.         newwidth += tree->tree.hpad + cc->tree.bbwidth;
  784.     }
  785.     }
  786.  
  787.  
  788.     tc->tree.bbsubwidth = newwidth;
  789.     tc->tree.bbsubheight = newheight;
  790.  
  791.     /*
  792.      * Now fit parent onto side (or top) of bounding box and correct for
  793.      * extra padding.  Be careful of unsigned arithmetic.
  794.      */
  795.     if (horiz) {
  796.     tc->tree.bbwidth += tree->tree.hpad + newwidth;
  797.     newheight -= tree->tree.vpad;
  798.     if (newheight > tc->tree.bbheight) tc->tree.bbheight = newheight;
  799.     } else {
  800.     tc->tree.bbheight += tree->tree.vpad + newheight;
  801.     newwidth -= tree->tree.hpad;
  802.     if (newwidth > tc->tree.bbwidth) tc->tree.bbwidth = newwidth;
  803.     }
  804. }
  805.  
  806.  
  807. static void set_positions (tw, w, level)
  808.      TreeWidget tw;
  809.      Widget w;
  810.      int level;
  811. {
  812.     int i;
  813.   
  814.     if (w) {
  815.     TreeConstraints tc = TREE_CONSTRAINT(w);
  816.  
  817.     if (level > 0) {
  818.         /*
  819.          * mirror if necessary
  820.          */
  821.         switch (tw->tree.gravity) {
  822.           case EastGravity:
  823.         tc->tree.x = (((Position) tw->tree.maxwidth) -
  824.                   ((Position) w->core.width) - tc->tree.x);
  825.         break;
  826.  
  827.           case SouthGravity:
  828.         tc->tree.y = (((Position) tw->tree.maxheight) -
  829.                   ((Position) w->core.height) - tc->tree.y);
  830.         break;
  831.         }
  832.  
  833.         /*
  834.          * Move the widget into position.
  835.          */
  836.         XtMoveWidget (w, tc->tree.x, tc->tree.y);
  837.     }
  838.  
  839.     /*
  840.      * Set the positions of all children.
  841.      */
  842.     for (i = 0; i < tc->tree.n_children; i++)
  843.       set_positions (tw, tc->tree.children[i], level + 1);
  844.     }
  845. }
  846.  
  847.  
  848. static void arrange_subtree (tree, w, depth, x, y)
  849.     TreeWidget tree;
  850.     Widget w;
  851.     int depth;
  852.     Position x, y;
  853. {
  854.     TreeConstraints tc = TREE_CONSTRAINT(w);  /* info attached to all kids */
  855.     TreeConstraints firstcc, lastcc;
  856.     register int i;
  857.     int newx, newy;
  858.     Bool horiz = IsHorizontal (tree);
  859.     Widget child = NULL;
  860.     Dimension tmp;
  861.     Dimension bw2 = w->core.border_width * 2;
  862.     Bool relayout = True;
  863.  
  864.  
  865.     /*
  866.      * If no children, then just lay out where requested.
  867.      */
  868.     tc->tree.x = x;
  869.     tc->tree.y = y;
  870.  
  871.     if (horiz) {
  872.     int myh = (w->core.height + bw2);
  873.  
  874.     if (myh > (int)tc->tree.bbsubheight) {
  875.         y += (myh - (int)tc->tree.bbsubheight) / 2;
  876.         relayout = False;
  877.     }
  878.     } else {
  879.     int myw = (w->core.width + bw2);
  880.  
  881.     if (myw > (int)tc->tree.bbsubwidth) {
  882.         x += (myw - (int)tc->tree.bbsubwidth) / 2;
  883.         relayout = False;
  884.     }
  885.     }
  886.  
  887.     if ((tmp = ((Dimension) x) + tc->tree.bbwidth) > tree->tree.maxwidth)
  888.       tree->tree.maxwidth = tmp;
  889.     if ((tmp = ((Dimension) y) + tc->tree.bbheight) > tree->tree.maxheight)
  890.       tree->tree.maxheight = tmp;
  891.  
  892.     if (tc->tree.n_children == 0) return;
  893.  
  894.  
  895.     /*
  896.      * Have children, so walk down tree laying out children, then laying
  897.      * out parents.
  898.      */
  899.     if (horiz) {
  900.     newx = x + tree->tree.largest[depth];
  901.     if (depth > 0) newx += tree->tree.hpad;
  902.     newy = y;
  903.     } else {
  904.     newx = x;
  905.     newy = y + tree->tree.largest[depth];
  906.     if (depth > 0) newy += tree->tree.vpad;
  907.     }
  908.  
  909.     for (i = 0; i < tc->tree.n_children; i++) {
  910.     TreeConstraints cc;
  911.  
  912.     child = tc->tree.children[i];    /* last value is used outside loop */
  913.     cc = TREE_CONSTRAINT(child);
  914.  
  915.     arrange_subtree (tree, child, depth + 1, newx, newy);
  916.     if (horiz) {
  917.         newy += tree->tree.vpad + cc->tree.bbheight;
  918.     } else {
  919.         newx += tree->tree.hpad + cc->tree.bbwidth;
  920.     }
  921.     }
  922.  
  923.     /*
  924.      * now layout parent between first and last children
  925.      */
  926.     if (relayout) {
  927.     Position adjusted;
  928.     firstcc = TREE_CONSTRAINT (tc->tree.children[0]);
  929.     lastcc = TREE_CONSTRAINT (child);
  930.  
  931.     /* Adjustments are disallowed if they result in a position above
  932.          * or to the left of the originally requested position, because
  933.      * this could collide with the position of the previous sibling.
  934.      */
  935.     if (horiz) {
  936.         tc->tree.x = x;
  937.         adjusted = firstcc->tree.y +
  938.           ((lastcc->tree.y + (Position) child->core.height + 
  939.         (Position) child->core.border_width * 2 -
  940.         firstcc->tree.y - (Position) w->core.height - 
  941.         (Position) w->core.border_width * 2 + 1) / 2);
  942.         if (adjusted > tc->tree.y) tc->tree.y = adjusted;
  943.     } else {
  944.         adjusted = firstcc->tree.x +
  945.           ((lastcc->tree.x + (Position) child->core.width +
  946.         (Position) child->core.border_width * 2 -
  947.         firstcc->tree.x - (Position) w->core.width -
  948.         (Position) w->core.border_width * 2 + 1) / 2);
  949.         if (adjusted > tc->tree.x) tc->tree.x = adjusted;
  950.         tc->tree.y = y;
  951.     }
  952.     }
  953. }
  954.  
  955. static void set_tree_size (tw, insetvalues, width, height)
  956.     TreeWidget tw;
  957.     Boolean insetvalues;
  958.     Dimension width, height;
  959. {
  960.     if (insetvalues) {
  961.     tw->core.width = width;
  962.     tw->core.height = height;
  963.     } else {
  964.     Dimension replyWidth = 0, replyHeight = 0;
  965.     XtGeometryResult result = XtMakeResizeRequest ((Widget) tw,
  966.                                width, height,
  967.                                &replyWidth,
  968.                                &replyHeight);
  969.     /*
  970.      * Accept any compromise.
  971.      */
  972.     if (result == XtGeometryAlmost)
  973.       XtMakeResizeRequest ((Widget) tw, replyWidth, replyHeight,
  974.                    (Dimension *) NULL, (Dimension *) NULL);
  975.     }
  976.     return;
  977. }
  978.  
  979. static void layout_tree (tw, insetvalues)
  980.     TreeWidget tw;
  981.     Boolean insetvalues;
  982. {
  983.     int i;
  984.     Dimension *dp;
  985.  
  986.     /*
  987.      * Do a depth-first search computing the width and height of the bounding
  988.      * box for the tree at that position (and below).  Then, walk again using
  989.      * this information to layout the children at each level.
  990.      */
  991.  
  992.     if (tw->tree.tree_root == NULL)
  993.     return;
  994.  
  995.     tw->tree.maxwidth = tw->tree.maxheight = 0;
  996.     for (i = 0, dp = tw->tree.largest; i < tw->tree.n_largest; i++, dp++)
  997.       *dp = 0;
  998.     initialize_dimensions (&tw->tree.largest, &tw->tree.n_largest, 
  999.                tw->tree.n_largest);
  1000.     compute_bounding_box_subtree (tw, tw->tree.tree_root, 0);
  1001.  
  1002.    /*
  1003.     * Second pass to do final layout.  Each child's bounding box is stacked
  1004.     * on top of (if horizontal, else next to) on top of its siblings.  The
  1005.     * parent is centered between the first and last children.
  1006.     */
  1007.     arrange_subtree (tw, tw->tree.tree_root, 0, 0, 0);
  1008.  
  1009.     /*
  1010.      * Move each widget into place.
  1011.      */
  1012.     set_tree_size (tw, insetvalues, tw->tree.maxwidth, tw->tree.maxheight);
  1013.     set_positions (tw, tw->tree.tree_root, 0);
  1014.  
  1015.     /*
  1016.      * And redisplay.
  1017.      */
  1018.     if (XtIsRealized ((Widget) tw)) {
  1019.     XClearArea (XtDisplay(tw), XtWindow((Widget)tw), 0, 0, 0, 0, True);
  1020.     }
  1021. }
  1022.  
  1023.  
  1024.  
  1025. /*****************************************************************************
  1026.  *                                                                           *
  1027.  *                 Public Routines                              *
  1028.  *                                                                           *
  1029.  *****************************************************************************/
  1030.  
  1031. void
  1032. #if NeedFunctionPrototypes
  1033. TreeForceLayout (Widget tree)
  1034. #else
  1035. TreeForceLayout (tree)
  1036.     Widget tree;
  1037. #endif
  1038. {
  1039.     layout_tree ((TreeWidget) tree, FALSE);
  1040. }
  1041.  
  1042.  
  1043.